package com.stars.easyms.logger.support.logback.util;

import ch.qos.logback.core.Appender;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import com.stars.easyms.logger.support.logback.handler.EasyMsLoggingEvent;
import com.stars.easyms.logger.support.logback.pattern.EasyMsPatternLayoutEncoder;
import com.stars.easyms.base.util.ClassExistsUtil;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>className: EasyMsAsynAppenderUtil</p>
 * <p>description: 异步Appender的工具类</p>
 *
 * @author guoguifang
 * @date 2019-08-20 15:35
 * @since 1.3.0
 */
public final class EasyMsAsynAppenderUtil {

    /**
     * 由于1.1版本和1.2版本的logback的解析类的解析方法不同，因此动态判断对应的方法
     */
    private static final Map<Class, Method> ENCODER_METHOD_CACHE = new ConcurrentHashMap<>(8);

    private static final Map<LayoutWrappingEncoder, EasyMsPatternLayoutEncoder> EASY_MS_ENCODER_CACHE = new ConcurrentHashMap<>(16);

    private static Boolean existsLogStashLogback;

    public static void writeOut(EasyMsLoggingEvent event, final Appender appender, Encoder encoder) {
        byte[] byteArray = null;
        if (encoder instanceof EasyMsPatternLayoutEncoder) {
            byteArray = ((EasyMsPatternLayoutEncoder) encoder).encodes(event);
        } else if (encoder instanceof LayoutWrappingEncoder) {
            byteArray = EASY_MS_ENCODER_CACHE.computeIfAbsent((LayoutWrappingEncoder) encoder, e -> new EasyMsPatternLayoutEncoder(e, appender)).encodes(event);
            // 防止内存溢出，当超出256个时清空重新创建
            if (EASY_MS_ENCODER_CACHE.size() > 256) {
                EASY_MS_ENCODER_CACHE.clear();
            }
        } else if (isLogstashEncoder(encoder)) {
            byteArray = LogStashLogbackUtil.encode(event, encoder);
        } else {
            Method encodeMethod = ENCODER_METHOD_CACHE.computeIfAbsent(encoder.getClass(), EasyMsAsynAppenderUtil::getEncodeMethod);
            Object obj = ReflectionUtils.invokeMethod(encodeMethod, encoder, event);
            if (obj instanceof byte[]) {
                byteArray = (byte[]) obj;
            }
        }
        if (byteArray != null && byteArray.length != 0) {
            EasyMsEventByteHolder.getEventByteList().add(byteArray);
        }
    }

    private static Method getEncodeMethod(Class clazz) {
        // 为了防止两个方法都存在，先遍历所有方法获取优先级高的encode方法，若没有encode方法再遍历所有方法获取低版本doEncode方法
        for (Method method : clazz.getMethods()) {
            if ("encode".equals(method.getName())) {
                Class<?>[] paramTypes = method.getParameterTypes();
                Class<?> returnType = method.getReturnType();
                if (paramTypes.length == 1 && paramTypes[0] == Object.class && returnType == byte[].class) {
                    return method;
                }
            }
        }
        for (Method method : clazz.getMethods()) {
            if ("doEncode".equals(method.getName())) {
                Class<?>[] paramTypes = method.getParameterTypes();
                Class<?> returnType = method.getReturnType();
                if (paramTypes.length == 1 && paramTypes[0] == Object.class && returnType == void.class) {
                    return method;
                }
            }
        }
        throw new IllegalStateException("Should never get here");
    }

    private static boolean isLogstashEncoder(Encoder encoder) {
        if (existsLogStashLogback == null) {
            existsLogStashLogback = ClassExistsUtil.isExist("net.logstash.logback.encoder.CompositeJsonEncoder");
        }
        return existsLogStashLogback && LogStashLogbackUtil.isLogstashEncoder(encoder);
    }

    private EasyMsAsynAppenderUtil() {
    }
}